#include "MemPool.h"

#include <assert.h>
#include <string.h>

MemPool* MemPool::getInstance(){
    static MemPool instance;

    return &instance;
}

MemPool::MemPool(){
    int i;
    for(i=0;i<MAX_HEAD;i++){
        m_block_head[i].m_free_list = NULL;
        m_block_head[i].m_free_count = 0;
    }

//    memset(m_block_head,0,sizeof(BlockHead) * MAX_HEAD);
}

MemPool::~MemPool(){
    int i;
    BlockHead* pHead;
    MemBlock* pBlock;
    MemBlock* block;

    for(i=0;i<MAX_HEAD;i++){
        pHead = &m_block_head[i];
        pBlock = pHead->m_free_list;
        while(pBlock){
            block = pBlock;
            pBlock = pBlock->m_next;
            free(block);
        }
    }
}

void* MemPool::_malloc(int n){
//    if(n == 0)
//        n = 1;
    if(n == 0) {
        return NULL;
    }

    int size = align(n,ALIGN);
    int k = (size >> 3) - 1;

    BlockHead* pHead;
    MemBlock* pBlock;

    if(size > MAX_MEM){
        pBlock = (MemBlock*)malloc(sizeof(MemBlock) + size);
        if(pBlock == NULL) {
            return NULL;
        }
        pBlock->m_head = NULL;
    }else{
        std::mutex* _mutex = &m_block_head[k]._mutex;
        std::unique_lock<std::mutex> _lock(*_mutex);

        pHead = &m_block_head[k];
        if(pHead->m_free_list == NULL){
            pBlock = (MemBlock*)malloc(sizeof(MemBlock) + size);
            if(pBlock == NULL) {
                return NULL;
            }
            pBlock->m_head = pHead;
            pBlock->m_next = NULL;
        }else{
            pBlock = pHead->m_free_list;
            if(pBlock->m_next != NULL){
                pHead->m_free_list = pBlock->m_next;
                pBlock->m_next = NULL;
            }else{
                pHead->m_free_list = NULL;
            }

            if(pHead->m_free_count > 0)
                pHead->m_free_count--;
        }
    }

    assert(pBlock != NULL);

    return (void*)pBlock->m_data;
}

void* MemPool::_calloc(int n){
    void* p = _malloc(n);
    if(p == NULL) {
        return NULL;
    }
    memset(p,0,n);
    return p;
}

void MemPool::_free(void *pData){
    BlockHead* pHead;
    MemBlock* pBlock;

    if(pData){
        pBlock = reinterpret_cast<MemBlock*>((char*)pData - sizeof(MemBlock));
        pHead = pBlock->m_head;
        if(pHead == NULL || pHead->m_free_count > 1000){
            free(pBlock);
        }else{
            std::mutex* _mutex = &pHead->_mutex;
            std::unique_lock<std::mutex> _lock(*_mutex);

            pBlock->m_next = pHead->m_free_list;
            pHead->m_free_list = pBlock;

            pHead->m_free_count++;
        }
    }
}

void *operator new(size_t size) {
    MemPool* pool = MemPool::getInstance();
    return pool->_malloc(size);
}

void *operator new[](size_t size) {
    return operator new(size);
}

void operator delete(void *ptr) throw() {
    MemPool* pool = MemPool::getInstance();
    if(ptr){
        pool->_free(ptr);
    }
}

void operator delete[](void *ptr) throw() {
    operator delete(ptr);
}
